package org.zjvis.datascience.common.exception.handler;

import cn.hutool.core.exceptions.ExceptionUtil;
import org.apache.shiro.ShiroException;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authz.UnauthorizedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.zjvis.datascience.common.exception.DataScienceException;
import org.zjvis.datascience.common.model.ApiResult;
import org.zjvis.datascience.common.model.ApiResultCode;

import javax.security.auth.login.LoginException;
import javax.validation.ValidationException;
import java.util.Objects;

/**
 * @description 处理异常
 * @date 2020-02-23
 */
@RestControllerAdvice
public class GlobalExceptionHandler {
    private final static Logger logger = LoggerFactory.getLogger("GlobalExceptionHandler");
    public static final int EXCEPTION_INFO_LIMIT = 100000;

    /**
     * UnauthorizedException
     */
    @ExceptionHandler(UnauthorizedException.class)
    public ResponseEntity<ApiResult> badCredentialsException(UnauthorizedException e) {
        // 打印堆栈信息
        logger.error(e.getMessage(), e);
        String message = "坏的凭证".equals(e.getMessage()) ? "用户名或密码不正确" : e.getMessage();
        return buildResponseEntity(HttpStatus.UNAUTHORIZED, ApiResult.valueOf(ApiResultCode.NO_AUTH, message));
    }

    /**
     * 处理自定义异常
     */
    @ExceptionHandler(value = DataScienceException.class)
    public ResponseEntity<ApiResult> badRequestException(DataScienceException e) {
        // 打印堆栈信息
        logger.error(e.getMessage(), e);
        if (e.getCause() != null) {
            e.getApiResult().setTips(ExceptionUtil.stacktraceToString(e, EXCEPTION_INFO_LIMIT));
        }
        return buildResponseEntity(HttpStatus.OK, e.getApiResult());
    }

    /**
     * 处理自定义异常
     */
    @ExceptionHandler(value = AuthenticationException.class)
    public ResponseEntity<ApiResult> badRequestException(AuthenticationException e) {
        // 打印堆栈信息
        logger.error(e.getMessage(), e);
        return buildResponseEntity(HttpStatus.OK, ApiResult.valueOf(ApiResultCode.NO_AUTH, "无权访问"));
    }

    /**
     * shiro 异常捕捉
     */
    @ExceptionHandler(value = ShiroException.class)
    public ResponseEntity<ApiResult> accountException(ShiroException e) {
        // 打印堆栈信息
        logger.error(e.getMessage(), e);
        ResponseEntity<ApiResult> responseEntity;
        if (e instanceof IncorrectCredentialsException) {
            responseEntity = buildResponseEntity(HttpStatus.OK, ApiResult.valueOf(ApiResultCode.PARAM_ERROR, "密码不正确"));
        } else if (e instanceof UnknownAccountException) {
            responseEntity = buildResponseEntity(HttpStatus.OK, ApiResult.valueOf(ApiResultCode.PARAM_ERROR, "此账户不存在"));
        } else if (e instanceof LockedAccountException) {
            responseEntity = buildResponseEntity(HttpStatus.OK, ApiResult.valueOf(ApiResultCode.PARAM_ERROR, "未知的账号"));
        } else {
            responseEntity = buildResponseEntity(HttpStatus.OK, ApiResult.valueOf(ApiResultCode.NO_AUTH, "无权访问"));
        }
        return responseEntity;
    }

    /**
     * 处理自定义异常
     */
    @ExceptionHandler(value = LoginException.class)
    public ResponseEntity<ApiResult> loginException(LoginException e) {
        // 打印堆栈信息
        logger.error(e.getMessage(), e);
        return buildResponseEntity(HttpStatus.UNAUTHORIZED, ApiResult.valueOf(ApiResultCode.NO_AUTH, e.getMessage()));
    }

    /**
     * 处理验证码错误
     */
//    @ExceptionHandler(value = CaptchaException.class)
//    public ResponseEntity<ApiResult> captchaException(CaptchaException e) {
//        // 打印堆栈信息
//        LogUtil.error(LogEnum.SYS_ERR, e);
//        return buildResponseEntity(HttpStatus.OK, e.getResponseBody());
//    }
    @ExceptionHandler(BindException.class)
    public ResponseEntity<ApiResult> bindException(BindException e) {
        // 打印堆栈信息
        logger.error(e.getMessage(), e);
        ObjectError error = e.getAllErrors().get(0);
        return buildResponseEntity(HttpStatus.BAD_REQUEST, ApiResult.valueOf(ApiResultCode.PARAM_ERROR, error.getDefaultMessage()));
    }

    @ExceptionHandler(ValidationException.class)
    public ResponseEntity<ApiResult> validationException(ValidationException e) {
        logger.error(e.getMessage(), e);
        return buildResponseEntity(HttpStatus.BAD_REQUEST, ApiResult.valueOf(ApiResultCode.PARAM_ERROR, e.getLocalizedMessage()));
    }

    /**
     * 处理所有接口数据验证异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ApiResult> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        // 打印堆栈信息
        logger.error(e.getMessage(), e);
        String[] str = Objects.requireNonNull(e.getBindingResult().getAllErrors().get(0).getCodes())[1].split("\\.");
        String message = e.getBindingResult().getAllErrors().get(0).getDefaultMessage();
        String msg = "不能为空";
        if (msg.equals(message)) {
            message = str[1] + ":" + message;
        }
        return buildResponseEntity(HttpStatus.BAD_REQUEST, ApiResult.valueOf(ApiResultCode.PARAM_ERROR, message));
    }

    /**
     * 处理所有不可知的异常
     */
    @ExceptionHandler(value = Throwable.class)
    public ResponseEntity<ApiResult> handleException(Throwable e) {
        // 打印堆栈信息
        logger.error(e.getMessage(), e);
        return buildResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR, ApiResult.valueOf(ApiResultCode.SYS_ERROR, e.getMessage())
        );
    }

    @ExceptionHandler(value = MissingServletRequestParameterException.class)
    public ResponseEntity<ApiResult> handleMissingServletRequestParameterException(MissingServletRequestParameterException e) {
        logger.error(e.getMessage(), e);
        return buildResponseEntity(HttpStatus.BAD_REQUEST, ApiResult.valueOf(ApiResultCode.PARAM_ERROR, e.getMessage())
        );
    }

    /**
     * 统一返回
     *
     * @param httpStatus
     * @param apiResult
     * @return
     */
    private ResponseEntity<ApiResult> buildResponseEntity(HttpStatus httpStatus, ApiResult apiResult) {
        if (apiResult.getResult() != null && apiResult.getResult() instanceof String) {
            apiResult.setMessage(apiResult.getMessage() + " " + apiResult.getResult());
        }
        return new ResponseEntity<>(apiResult, httpStatus);
    }

}
